package com.google.code.memoriesalbum.util;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.StringUtils;

import com.google.appengine.api.datastore.KeyFactory;
import com.google.code.memoriesalbum.MediaByteArraySource;
import com.google.code.memoriesalbum.model.PicasaAlbum;
import com.google.code.memoriesalbum.model.PicasaPhoto;
import com.google.gdata.client.GoogleService;
import com.google.gdata.client.authn.oauth.GoogleOAuthParameters;
import com.google.gdata.client.authn.oauth.OAuthException;
import com.google.gdata.client.photos.PicasawebService;
import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.photos.AlbumEntry;
import com.google.gdata.data.photos.PhotoEntry;
import com.google.gdata.data.photos.UserFeed;
import com.google.gdata.util.ServiceException;

/**
 * {@link PicasawebService}を操作するユーティリティクラス.
 * @author shin1ogawa
 */
public class PicasaUtil {

	private PicasaUtil() {
	}

	/**
	 * {@link PicasawebService}のオブジェクトにOAuthを使用した認証情報を設定する。
	 * @param service
	 * @param token 
	 * @param tokenSecret 
	 * @throws OAuthException
	 * @see GoogleOAuthParameters
	 * @see PicasawebService#setOAuthCredentials(com.google.gdata.client.authn.oauth.OAuthParameters, com.google.gdata.client.authn.oauth.OAuthSigner)
	 */
	public static void setOAuthCredentials(GoogleService service, String token, String tokenSecret)
			throws OAuthException {
		GoogleOAuthParameters params = new GoogleOAuthParameters();
		params.setOAuthConsumerKey(Constants.CONSUMER_KEY);
		params.setOAuthConsumerSecret(Constants.CONSUMER_SECRET);
		params.setOAuthSignatureMethod("HMAC-SHA1");
		params.setOAuthToken(token);
		params.setOAuthTokenSecret(tokenSecret);
		params.addCustomBaseParameter("oauth_version", "1.0");
		service.setOAuthCredentials(params,
				new com.google.gdata.client.authn.oauth.OAuthHmacSha1Signer());
	}

	/**
	 * Picasa上に新しいAlbumを登録する。
	 * @param service PicasawebService
	 * @param user userid
	 * @param album 各属性が設定された{@link PicasaAlbum}
	 * @return AlbumEntry
	 * @throws MalformedURLException
	 * @throws IOException
	 * @throws ServiceException
	 */
	public static AlbumEntry createAlbum(PicasawebService service, String user, PicasaAlbum album)
			throws MalformedURLException, IOException, ServiceException {
		URL postUrl = new URL("http://picasaweb.google.com/data/feed/api/user/" + user);
		AlbumEntry newAlbumEntry = toAlbumEntry(album);
		AlbumEntry insertedAlbumEntry = service.insert(postUrl, newAlbumEntry);
		return insertedAlbumEntry;
	}

	/**
	 * AlbumIdを指定してアルバムを削除する。
	 * @param service
	 * @param user
	 * @param id アルバムID. 例：{@literal "5432581274809182721"}
	 * @throws MalformedURLException
	 * @throws IOException
	 * @throws ServiceException
	 */
	public static void deleteAlbumById(PicasawebService service, String user, String id)
			throws MalformedURLException, IOException, ServiceException {
		if (user.indexOf('@') > 0) {
			user = user.split("@")[0];
		}
		String entryUrl =
				String.format("http://picasaweb.google.com/data/entry/api/user/%s/albumid/%s",
						user, id);
		AlbumEntry albumEntry = service.getEntry(new URL(entryUrl), AlbumEntry.class);
		albumEntry.delete();
	}

	/**
	 * アルバム一覧を取得する。
	 * @param service PicasawebService
	 * @param user userid
	 * @return {@link PicasaAlbum}のリスト
	 * @throws IOException
	 * @throws ServiceException
	 */
	public static List<PicasaAlbum> getAlbumList(PicasawebService service, String user)
			throws IOException, ServiceException {
		List<PicasaAlbum> list = new ArrayList<PicasaAlbum>();
		URL feedUrl =
				new URL("http://picasaweb.google.com/data/feed/api/user/" + user + "?kind=album");
		UserFeed feed = service.getFeed(feedUrl, UserFeed.class);
		for (AlbumEntry albumEntry : feed.getAlbumEntries()) {
			list.add(toPicasaAlbum(albumEntry));
		}
		return list;
	}

	/**
	 * タイトルを指定してアルバムを検索する。
	 * @param service
	 * @param user
	 * @param title
	 * @return AlbumEntry
	 * @throws IOException
	 * @throws ServiceException
	 */
	public static AlbumEntry searchAlbumEntryByTitle(PicasawebService service, String user,
			String title) throws IOException, ServiceException {
		URL feedUrl =
				new URL("http://picasaweb.google.com/data/feed/api/user/" + user + "?kind=album");
		UserFeed feed = service.getFeed(feedUrl, UserFeed.class);
		for (AlbumEntry albumEntry : feed.getAlbumEntries()) {
			if (StringUtils.equals(albumEntry.getTitle().getPlainText(), title)) {
				return albumEntry;
			}
		}
		return null;
	}

	/**
	 * アルバムに写真をアップロードする。
	 * @param service PicasawebService
	 * @param user userid
	 * @param newPhoto 各属性が設定された{@link PicasaPhoto}
	 * @return PicasaPhoto
	 * @throws ServiceException 
	 * @throws IOException 
	 */
	public static PhotoEntry createPhoto(PicasawebService service, String user, PicasaPhoto newPhoto)
			throws IOException, ServiceException {
		URL postUrl =
				new URL("http://picasaweb.google.com/data/feed/api/user/" + user + "/albumid/"
						+ newPhoto.getAlbumId());
		PhotoEntry newPhotoEntry = toPhotoEntry(newPhoto);
		newPhotoEntry.setClient(Constants.APPLICATION_NAME);
		// TODO 強制的に"memories-album"タグをつけたい。
		PhotoEntry insertedPhotoEntry = service.insert(postUrl, newPhotoEntry);
		return insertedPhotoEntry;
	}

	/**
	 * アルバムID、写真IDに対応する{@link PhotoEntry}を返す。
	 * @param service
	 * @param user
	 * @param albumId
	 * @param photoId
	 * @return PhotoEntry
	 * @throws IOException
	 * @throws ServiceException
	 */
	static PhotoEntry getPhotoEntry(PicasawebService service, String user, String albumId,
			String photoId) throws IOException, ServiceException {
		URL photoUrl =
				new URL("http://picasaweb.google.com/data/entry/api/user/" + user + "/albumid/"
						+ albumId + "/photoid/" + photoId);
		return service.getEntry(photoUrl, PhotoEntry.class);
	}

	static PhotoEntry toPhotoEntry(PicasaPhoto photo) {
		PhotoEntry photoEntry = new PhotoEntry();
		photoEntry.setTitle(new PlainTextConstruct(photo.getTitle()));
		photoEntry.setDescription(new PlainTextConstruct(photo.getDescription()));
		photoEntry.setMediaSource(new MediaByteArraySource(photo.getMediaSource(), photo
			.getMediaType()));
		if (photo.getPoint() != null) {
			photoEntry.setGeoLocation((double) photo.getPoint().getLatitude(), (double) photo
				.getPoint().getLongitude());
		}
		return photoEntry;
	}

	static PicasaPhoto toPicasaPhoto(PhotoEntry photoEntry) {
		PicasaPhoto photo = new PicasaPhoto();
		photo.setKey(KeyFactory.createKey(PicasaPhoto.class.getSimpleName(), photoEntry
			.getGphotoId()));
		photo.setAlbumId(photoEntry.getAlbumId());
		photo.setTitle(photoEntry.getTitle().getPlainText());
		photo.setDescription(photoEntry.getDescription().getPlainText());
		return photo;
	}

	static AlbumEntry toAlbumEntry(PicasaAlbum album) {
		AlbumEntry albumEntry = new AlbumEntry();
		albumEntry.setTitle(new PlainTextConstruct(album.getTitle()));
		albumEntry.setDescription(new PlainTextConstruct(album.getDescription()));
		albumEntry.setDate(album.getDate());
		return albumEntry;
	}

	/**
	 * GDataLibraryの{@link AlbumEntry}から{@link PicasaAlbum}を作成して返す。
	 * @param albumEntry
	 * @return {@code albumEntry}から作成された{@link PicasaAlbum}.
	 */
	public static PicasaAlbum toPicasaAlbum(AlbumEntry albumEntry) {
		if (albumEntry == null) {
			throw new IllegalArgumentException("albumEntry must not be null.");
		}
		PicasaAlbum album = new PicasaAlbum();
		album.setKey(KeyFactory.createKey(PicasaAlbum.class.getSimpleName(), albumEntry
			.getGphotoId()));
		album.setTitle(albumEntry.getTitle().getPlainText());
		album.setDescription(albumEntry.getDescription().getPlainText());
		album.setDate(albumEntry.getDate());
		return album;
	}
}
